﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace Black.Minecraft.Editor.Behaviors
{
    /// <summary>
    /// Behavior permettant de binder une commande sur un événement
    /// </summary>
    public class TextChangedBehavior : Behavior<TextBox>
    {
        #region Propriétés de dépendances

        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(TextChangedBehavior), new PropertyMetadata(null));
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(TextChangedBehavior), new PropertyMetadata(null));

        public static readonly DependencyProperty EnterKeyDownCommandProperty = DependencyProperty.Register("EnterKeyDownCommand", typeof(ICommand), typeof(TextChangedBehavior), new PropertyMetadata(null));
        public static readonly DependencyProperty EnterKeyDownCommandParameterProperty = DependencyProperty.Register("EnterKeyDownCommandParameter", typeof(object), typeof(TextChangedBehavior), new PropertyMetadata(null));

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(TextChangedBehavior), new PropertyMetadata(string.Empty, TextChangedBehavior.OnValueChanged));

        #endregion

        #region Propriétés

        /// <summary>
        /// Obtient ou définit la commande a éxécuter sur le TextChanged
        /// </summary>
        public ICommand Command
        {
            get
            {
                return ((ICommand)this.GetValue(TextChangedBehavior.CommandProperty));
            }
            set
            {
                this.SetValue(TextChangedBehavior.CommandProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit le paramètre de la commande a éxécuter sur le TextChanged
        /// </summary>
        public object CommandParameter
        {
            get
            {
                return (this.GetValue(TextChangedBehavior.CommandParameterProperty));
            }
            set
            {
                this.SetValue(TextChangedBehavior.CommandParameterProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit la commande a éxécuter sur l'appuie de la touche entrée
        /// </summary>
        public ICommand EnterKeyDownCommand
        {
            get
            {
                return ((ICommand)this.GetValue(TextChangedBehavior.EnterKeyDownCommandProperty));
            }
            set
            {
                this.SetValue(TextChangedBehavior.EnterKeyDownCommandProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit le paramètre de la commande a éxécuter sur l'appuie de la touche entrée
        /// </summary>
        public object EnterKeyDownCommandParameter
        {
            get
            {
                return (this.GetValue(TextChangedBehavior.EnterKeyDownCommandParameterProperty));
            }
            set
            {
                this.SetValue(TextChangedBehavior.EnterKeyDownCommandParameterProperty, value);
            }
        }

        /// <summary>
        /// Obtient ou définit la valeur de binding pour la propriété Text
        /// </summary>
        public string Value
        {
            get
            {
                return ((string)this.GetValue(TextChangedBehavior.ValueProperty));
            }
            set
            {
                this.SetValue(TextChangedBehavior.ValueProperty, value);
            }
        }

        #endregion

        #region Constructeurs

        /// <summary>
        /// Constructeur de la classe
        /// </summary>
        public TextChangedBehavior()
            : base()
        {
        }

        #endregion

        #region Fonctions protégées

        /// <summary>
        /// Se produit sur l'attachement entre l'instance et le behavior
        /// </summary>
        protected override void OnAttached()
        {
            // On initialise la valeur du texte

            if (this.Value != null)
                this.AssociatedObject.Text = this.Value;

            // On écoute les changements

            this.AssociatedObject.KeyDown += this.OnKeyDown;
            this.AssociatedObject.TextChanged += this.OnTextChanged;
        }

        /// <summary>
        /// Se produit sur le détachement entre l'instance et le behavior
        /// </summary>
        protected override void OnDetaching()
        {
            // On se détache

            this.AssociatedObject.KeyDown -= this.OnKeyDown;
            this.AssociatedObject.TextChanged -= this.OnTextChanged;
        }

        /// <summary>
        /// Se produit sur le changement de la propriété Value
        /// </summary>
        /// <param name="oldValue">Ancienne valeur</param>
        /// <param name="newValue">Nouvelle valeur</param>
        protected virtual void OnValueChanged(string oldValue, string newValue)
        {
            if (newValue != null)
                this.AssociatedObject.Text = newValue;
        }

        #endregion

        #region Fonctions privées

        /// <summary>
        /// Se produit sur un appuie touche
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter && this.EnterKeyDownCommand != null)
                this.EnterKeyDownCommand.Execute(this.EnterKeyDownCommandParameter == null ? e : this.EnterKeyDownCommandParameter);
        }

        /// <summary>
        /// Se produit sur le changement de texte
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            this.Value = this.AssociatedObject.Text;

            if (this.Command != null)
                this.Command.Execute(this.CommandParameter == null ? e : this.CommandParameter);
        }

        #endregion

        #region Fonctions statiques

        /// <summary>
        /// Se produit sur le changement de la propriété Value
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((TextChangedBehavior)sender).OnValueChanged((string)e.OldValue, (string)e.NewValue);
        }

        #endregion
    }
}
